},
getInitialResultSet: function(terms, callback, cancellable) {
- callback(this._appSys.initial_search(terms));
+ let query = terms.join(' ');
+ let groups = Gio.DesktopAppInfo.search(query);
+ let usage = Shell.AppUsage.get_default();
+ let results = [];
+ groups.forEach(function(group) {
+ group = group.filter(function(appID) {
+ let app = Gio.DesktopAppInfo.new(appID);
+ return app && app.should_show();
+ });
+ results = results.concat(group.sort(function(a, b) {
+ return usage.compare('', a, b);
+ }));
+ });
+ callback(results);
},
getSubsearchResultSet: function(previousResults, terms, callback, cancellable) {
- callback(this._appSys.subsearch(previousResults, terms));
+ this.getInitialResultSet(terms, callback, cancellable);
},
activateResult: function(result) {
void _shell_app_remove_window (ShellApp *app, MetaWindow *window);
-void _shell_app_do_match (ShellApp *app,
- GSList *terms,
- GSList **prefix_results,
- GSList **substring_results);
-
G_END_DECLS
#endif /* __SHELL_APP_PRIVATE_H__ */
return ret;
}
-
-
-static gint
-compare_apps_by_usage (gconstpointer a,
- gconstpointer b,
- gpointer data)
-{
- ShellAppUsage *usage = shell_app_usage_get_default ();
-
- ShellApp *app_a = (ShellApp*)a;
- ShellApp *app_b = (ShellApp*)b;
-
- return shell_app_usage_compare (usage, "", app_a, app_b);
-}
-
-static GSList *
-sort_and_concat_results (ShellAppSystem *system,
- GSList *prefix_matches,
- GSList *substring_matches)
-{
- GSList *matches = NULL;
- GSList *l;
-
- prefix_matches = g_slist_sort_with_data (prefix_matches,
- compare_apps_by_usage,
- system);
- substring_matches = g_slist_sort_with_data (substring_matches,
- compare_apps_by_usage,
- system);
-
- for (l = substring_matches; l != NULL; l = l->next)
- matches = g_slist_prepend (matches, (char *) shell_app_get_id (SHELL_APP (l->data)));
- for (l = prefix_matches; l != NULL; l = l->next)
- matches = g_slist_prepend (matches, (char *) shell_app_get_id (SHELL_APP (l->data)));
-
- return g_slist_reverse (matches);
-}
-
-/**
- * normalize_terms:
- * @terms: (element-type utf8): Input search terms
- *
- * Returns: (element-type utf8) (transfer full): Unicode-normalized and lowercased terms
- */
-static GSList *
-normalize_terms (GSList *terms)
-{
- GSList *normalized_terms = NULL;
- GSList *iter;
- for (iter = terms; iter; iter = iter->next)
- {
- const char *term = iter->data;
- normalized_terms = g_slist_prepend (normalized_terms,
- shell_util_normalize_casefold_and_unaccent (term));
- }
- return normalized_terms;
-}
-
-static GSList *
-search_tree (ShellAppSystem *self,
- GSList *terms,
- GHashTable *apps)
-{
- GSList *prefix_results = NULL;
- GSList *substring_results = NULL;
- GSList *normalized_terms;
- GHashTableIter iter;
- gpointer key, value;
-
- normalized_terms = normalize_terms (terms);
-
- g_hash_table_iter_init (&iter, apps);
- while (g_hash_table_iter_next (&iter, &key, &value))
- {
- ShellApp *app = value;
- _shell_app_do_match (app, normalized_terms,
- &prefix_results,
- &substring_results);
- }
- g_slist_free_full (normalized_terms, g_free);
-
- return sort_and_concat_results (self, prefix_results, substring_results);
-}
-
-/**
- * shell_app_system_initial_search:
- * @system: A #ShellAppSystem
- * @terms: (element-type utf8): List of terms, logical AND
- *
- * Search through applications for the given search terms.
- *
- * Returns: (transfer container) (element-type utf8): List of applications
- */
-GSList *
-shell_app_system_initial_search (ShellAppSystem *self,
- GSList *terms)
-{
- return search_tree (self, terms, self->priv->id_to_app);
-}
-
-/**
- * shell_app_system_subsearch:
- * @system: A #ShellAppSystem
- * @previous_results: (element-type utf8): List of previous results
- * @terms: (element-type utf8): List of terms, logical AND
- *
- * Search through a previous result set; for more information, see
- * js/ui/search.js. Note that returned strings are only valid until
- * a return to the main loop.
- *
- * Returns: (transfer container) (element-type utf8): List of application identifiers
- */
-GSList *
-shell_app_system_subsearch (ShellAppSystem *system,
- GSList *previous_results,
- GSList *terms)
-{
- GSList *iter;
- GSList *prefix_results = NULL;
- GSList *substring_results = NULL;
- GSList *normalized_terms = normalize_terms (terms);
-
- previous_results = g_slist_reverse (previous_results);
-
- for (iter = previous_results; iter; iter = iter->next)
- {
- ShellApp *app = shell_app_system_lookup_app (system, iter->data);
-
- _shell_app_do_match (app, normalized_terms,
- &prefix_results,
- &substring_results);
- }
- g_slist_free_full (normalized_terms, g_free);
-
- /* Note that a shorter term might have matched as a prefix, but
- when extended only as a substring, so we have to redo the
- sort rather than reusing the existing ordering */
- return sort_and_concat_results (system, prefix_results, substring_results);
-}
-
GSList *shell_app_system_get_running (ShellAppSystem *self);
-GSList *shell_app_system_initial_search (ShellAppSystem *system,
- GSList *terms);
-GSList *shell_app_system_subsearch (ShellAppSystem *system,
- GSList *previous_results,
- GSList *terms);
-
#endif /* __SHELL_APP_SYSTEM_H__ */
* shell_app_usage_compare:
* @self: the usage instance to request
* @context: Activity identifier
- * @app_a: First app
- * @app_b: Second app
+ * @id_a: ID of first app
+ * @id_b: ID of second app
*
- * Compare @app_a and @app_b based on frequency of use.
+ * Compare @id_a and @id_b based on frequency of use.
*
- * Returns: -1 if @app_a ranks higher than @app_b, 1 if @app_b ranks higher
- * than @app_a, and 0 if both rank equally.
+ * Returns: -1 if @id_a ranks higher than @id_b, 1 if @id_b ranks higher
+ * than @id_a, and 0 if both rank equally.
*/
int
shell_app_usage_compare (ShellAppUsage *self,
const char *context,
- ShellApp *app_a,
- ShellApp *app_b)
+ const char *id_a,
+ const char *id_b)
{
GHashTable *usages;
UsageData *usage_a, *usage_b;
if (usages == NULL)
return 0;
- usage_a = g_hash_table_lookup (usages, shell_app_get_id (app_a));
- usage_b = g_hash_table_lookup (usages, shell_app_get_id (app_b));
+ usage_a = g_hash_table_lookup (usages, id_a);
+ usage_b = g_hash_table_lookup (usages, id_b);
if (usage_a == NULL && usage_b == NULL)
return 0;
const char *context);
int shell_app_usage_compare (ShellAppUsage *self,
const char *context,
- ShellApp *app_a,
- ShellApp *app_b);
+ const char *id_a,
+ const char *id_b);
G_END_DECLS
ShellAppRunningState *running_state;
char *window_id_string;
-
- char *casefolded_name;
- char *casefolded_generic_name;
char *name_collation_key;
- char *casefolded_exec;
- char **casefolded_keywords;
};
enum {
return g_strndup (start, end - start);
}
-static void
-shell_app_init_search_data (ShellApp *app)
-{
- const char *name;
- const char *generic_name;
- const char *exec;
- const char * const *keywords;
- char *normalized_exec;
-
- name = g_app_info_get_name (G_APP_INFO (app->info));
- app->casefolded_name = shell_util_normalize_casefold_and_unaccent (name);
-
- generic_name = g_desktop_app_info_get_generic_name (app->info);
- if (generic_name)
- app->casefolded_generic_name = shell_util_normalize_casefold_and_unaccent (generic_name);
- else
- app->casefolded_generic_name = NULL;
-
- exec = g_app_info_get_executable (G_APP_INFO (app->info));
- normalized_exec = shell_util_normalize_casefold_and_unaccent (exec);
- app->casefolded_exec = trim_exec_line (normalized_exec);
- g_free (normalized_exec);
-
- keywords = g_desktop_app_info_get_keywords (app->info);
-
- if (keywords)
- {
- int i;
-
- app->casefolded_keywords = g_new0 (char*, g_strv_length ((char **)keywords) + 1);
-
- i = 0;
- while (keywords[i])
- {
- app->casefolded_keywords[i] = shell_util_normalize_casefold_and_unaccent (keywords[i]);
- ++i;
- }
- app->casefolded_keywords[i] = NULL;
- }
- else
- app->casefolded_keywords = NULL;
-}
-
/**
* shell_app_compare_by_name:
* @app: One app
return strcmp (app->name_collation_key, other->name_collation_key);
}
-static ShellAppSearchMatch
-_shell_app_match_search_terms (ShellApp *app,
- GSList *terms)
-{
- GSList *iter;
- ShellAppSearchMatch match;
-
- if (G_UNLIKELY (!app->casefolded_name))
- shell_app_init_search_data (app);
-
- match = MATCH_NONE;
- for (iter = terms; iter; iter = iter->next)
- {
- ShellAppSearchMatch current_match;
- const char *term = iter->data;
- const char *p;
-
- current_match = MATCH_NONE;
-
- p = strstr (app->casefolded_name, term);
- if (p != NULL)
- {
- if (p == app->casefolded_name || *(p - 1) == ' ')
- current_match = MATCH_PREFIX;
- else
- current_match = MATCH_SUBSTRING;
- }
-
- if (app->casefolded_generic_name)
- {
- p = strstr (app->casefolded_generic_name, term);
- if (p != NULL)
- {
- if (p == app->casefolded_generic_name || *(p - 1) == ' ')
- current_match = MATCH_PREFIX;
- else if (current_match < MATCH_PREFIX)
- current_match = MATCH_SUBSTRING;
- }
- }
-
- if (app->casefolded_exec)
- {
- p = strstr (app->casefolded_exec, term);
- if (p != NULL)
- {
- if (p == app->casefolded_exec || *(p - 1) == '-')
- current_match = MATCH_PREFIX;
- else if (current_match < MATCH_PREFIX)
- current_match = MATCH_SUBSTRING;
- }
- }
-
- if (app->casefolded_keywords)
- {
- int i = 0;
- while (app->casefolded_keywords[i] && current_match < MATCH_PREFIX)
- {
- p = strstr (app->casefolded_keywords[i], term);
- if (p != NULL)
- {
- if (p == app->casefolded_keywords[i])
- current_match = MATCH_PREFIX;
- else
- current_match = MATCH_SUBSTRING;
- }
- ++i;
- }
- }
-
- if (current_match == MATCH_NONE)
- return current_match;
-
- if (current_match > match)
- match = current_match;
- }
- return match;
-}
-
-void
-_shell_app_do_match (ShellApp *app,
- GSList *terms,
- GSList **prefix_results,
- GSList **substring_results)
-{
- ShellAppSearchMatch match;
-
- g_assert (app != NULL);
-
- /* Skip window-backed apps */
- if (app->info == NULL)
- return;
- /* Skip not-visible apps */
- if (!g_app_info_should_show (G_APP_INFO (app->info)))
- return;
-
- match = _shell_app_match_search_terms (app, terms);
- switch (match)
- {
- case MATCH_NONE:
- break;
- case MATCH_PREFIX:
- *prefix_results = g_slist_prepend (*prefix_results, app);
- break;
- case MATCH_SUBSTRING:
- *substring_results = g_slist_prepend (*substring_results, app);
- break;
- }
-}
-
-
static void
shell_app_init (ShellApp *self)
{
g_free (app->window_id_string);
- g_free (app->casefolded_name);
- g_free (app->casefolded_generic_name);
g_free (app->name_collation_key);
- g_free (app->casefolded_exec);
- g_strfreev (app->casefolded_keywords);
G_OBJECT_CLASS(shell_app_parent_class)->finalize (object);
}
box->y2 = y_max;
}
-char *
-shell_util_normalize_and_casefold (const char *str)
-{
- char *normalized, *result;
-
- if (str == NULL)
- return NULL;
-
- /* NOTE: 'ALL' is equivalent to 'NFKD'. If this is ever updated, please
- * update the unaccenting mechanism as well. */
- normalized = g_utf8_normalize (str, -1, G_NORMALIZE_ALL);
- result = g_utf8_casefold (normalized, -1);
- g_free (normalized);
- return result;
-}
-
-/* Combining diacritical mark?
- * Basic range: [0x0300,0x036F]
- * Supplement: [0x1DC0,0x1DFF]
- * For Symbols: [0x20D0,0x20FF]
- * Half marks: [0xFE20,0xFE2F]
- */
-#define IS_CDM_UCS4(c) (((c) >= 0x0300 && (c) <= 0x036F) || \
- ((c) >= 0x1DC0 && (c) <= 0x1DFF) || \
- ((c) >= 0x20D0 && (c) <= 0x20FF) || \
- ((c) >= 0xFE20 && (c) <= 0xFE2F))
-
-/* Copied from tracker/src/libtracker-fts/tracker-parser-glib.c under the GPL
- * Originally written by Aleksander Morgado <aleksander@gnu.org>
- */
-char *
-shell_util_normalize_casefold_and_unaccent (const char *str)
-{
- char *tmp;
- gsize i = 0, j = 0, ilen;
-
- if (str == NULL)
- return NULL;
-
- /* Get the NFKD-normalized and casefolded string */
- tmp = shell_util_normalize_and_casefold (str);
- ilen = strlen (tmp);
-
- while (i < ilen)
- {
- gunichar unichar;
- gchar *next_utf8;
- gint utf8_len;
-
- /* Get next character of the word as UCS4 */
- unichar = g_utf8_get_char_validated (&tmp[i], -1);
-
- /* Invalid UTF-8 character or end of original string. */
- if (unichar == (gunichar) -1 ||
- unichar == (gunichar) -2)
- {
- break;
- }
-
- /* Find next UTF-8 character */
- next_utf8 = g_utf8_next_char (&tmp[i]);
- utf8_len = next_utf8 - &tmp[i];
-
- if (IS_CDM_UCS4 ((guint32) unichar))
- {
- /* If the given unichar is a combining diacritical mark,
- * just update the original index, not the output one */
- i += utf8_len;
- continue;
- }
-
- /* If already found a previous combining
- * diacritical mark, indexes are different so
- * need to copy characters. As output and input
- * buffers may overlap, need to use memmove
- * instead of memcpy */
- if (i != j)
- {
- memmove (&tmp[j], &tmp[i], utf8_len);
- }
-
- /* Update both indexes */
- i += utf8_len;
- j += utf8_len;
- }
-
- /* Force proper string end */
- tmp[j] = '\0';
-
- return tmp;
-}
-
/**
* shell_util_format_date:
* @format: a strftime-style string format, as parsed by
int shell_util_get_week_start (void);
-char *shell_util_normalize_and_casefold (const char *str);
-
-char *shell_util_normalize_casefold_and_unaccent (const char *str);
-
char *shell_util_format_date (const char *format,
gint64 time_ms);